package com.configurator;

import com.appclient.DaoInstanceEnum;
import com.carpark.ejb.DAO.DAO;
import com.carpark.ejb.Model.ParkingspacesModel;
import com.wizardOther.OtherControlledScreen;
import com.wizardOther.OtherScreensController;
import configurationReader.SpacesReader;
import configurationReader.SpacesWritter;
import exceptions.LevelExistException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Comparator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import popup.PopupController;
import popup.PopupSpaceSettingsController;

/**
 *
 * @author Sawior
 */
public class ConfiguratorPanelController implements Initializable, OtherControlledScreen {

    private static final String EMPTY_STRING = "";
    private static final String CHOOSE_LEVEL_INFO = "Wybierz poziom";
    private static final String COLUMNS_INFO = "Wpisz liczbę kolumn";
    private static final String ROWS_INFO = "Wpisz liczbę wierszy";
    private static final String GOOD_INFO_STYLE = "textField";
    private static final String WRONG_INFO_STYLE = "textFieldWrong";
    private static final String SPACE_DEFAULT_STYLE = "space-creator-new";
    private static final int MAX_SIZE = 100;
    private static final int MIN_SIZE = 0;
    private static final int PREF_GRID_WIDTH = 1350;
    private OtherScreensController myController;
    private HashMap<String, Tab> tabs = new HashMap<>();
    private HashMap<Integer, FieldData> fields = new HashMap<>();
    @FXML
    private TabPane levelTabs;
    @FXML
    private Tab addTab;
    @FXML
    private Button addSpace;
    @FXML
    private TextField spaceRows;
    @FXML
    private TextField spaceColumns;
    @FXML
    private ComboBox spaceLevel;
    @FXML
    private Label spaceLevelInfo;
    @FXML
    private Label spaceRowInfo;
    @FXML
    private Label spaceColumnInfo;
    @FXML
    private Button saveLevels;
    @FXML
    private Button loadLevels;
    @FXML
    private CheckBox saveConfirm;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
    }

    
    @FXML
    private void loadFromFile(ActionEvent event) {
        if(event.getSource() != loadLevels){
            return;
        }
        Map<Integer, FieldData> data = new HashMap(SpacesReader.loadSpacesFile());
        List<Integer> levels = getLevels(data);
        for (Integer level : levels) {
            Integer[] maxs = getFieldsForLevel(data, level);
            Integer maxColumn = maxs[0]+1;
            Integer maxRow = maxs[1]+1;
            List<FieldData> fields = getFieldsForLevel(data, level, maxRow, maxColumn);
            createNewTab(level.toString(), maxColumn, maxRow, fields);
        }
    }

    private List<FieldData> getFieldsForLevel(Map<Integer, FieldData> dataFields, Integer level, Integer maxRow, Integer maxColumn) {
        List<FieldData> data = new ArrayList<>();
        for (Integer key : dataFields.keySet()) {
            if (dataFields.get(key).getLevel() == level) {
                data.add(dataFields.get(key));
                if (dataFields.get(key).getX() > maxColumn) {
                    maxColumn = dataFields.get(key).getX();
                }
                if (dataFields.get(key).getY() > maxRow) {
                    maxRow = dataFields.get(key).getY();
                }
            }
        }

        return data;
    }
    
    private Integer[] getFieldsForLevel(Map<Integer, FieldData> dataFields, Integer level) {
        int maxColumn = 0;
        int maxRow = 0;
        List<FieldData> data = new ArrayList<>();
        for (Integer key : dataFields.keySet()) {
            if (dataFields.get(key).getLevel() == level) {
                data.add(dataFields.get(key));
                if (dataFields.get(key).getX() > maxColumn) {
                    maxColumn = dataFields.get(key).getX();
                }
                if (dataFields.get(key).getY() > maxRow) {
                    maxRow = dataFields.get(key).getY();
                }
            }
        }

        return new Integer[]{maxColumn, maxRow};
    }

    private List<Integer> getLevels(Map<Integer, FieldData> fields) {
        List<Integer> levels = new ArrayList<>();

        for (Integer key : fields.keySet()) {
            Integer level = fields.get(key).getLevel();
            if (!levels.contains(level)) {
                levels.add(level);
            }
        }
        return levels;
    }

    @Override
    public void setScreenParent(OtherScreensController screenParent) {
        myController = screenParent;
    }

    @FXML
    private void saveLevels(ActionEvent event) {
        if (event.getSource() == saveLevels) {
            if (saveConfirm.isSelected()) {
                if (saveToDataBase()) {
                    SpacesWritter.writeSpaces(fields);
                    createWarningPopup("Zapisano.");
                } else {
                    createWarningPopup("Nie udało się zapisać stanowisk.");
                }
            } else {
                createWarningPopup("Potwierdź wykonanie operacji.");
            }
        }
    }

    private boolean saveToDataBase() {
        DAO dao = DaoInstanceEnum.INSTANCE.getDao();

        List<ParkingspacesModel> spacesToUpdate = new ArrayList<>();

        for (Integer key : fields.keySet()) {
            ParkingspacesModel currentSpace = new ParkingspacesModel(key);
            currentSpace.setStatus(fields.get(key).getSpaceStatusToXML());
            currentSpace.setType(fields.get(key).getSpaceTypeToXML());
            spacesToUpdate.add(currentSpace);
        }

        return dao.synchronizeParkingSpacesWithConfiguration(spacesToUpdate);
    }

    @FXML
    private void handleAction(ActionEvent event) {
        if (event.getSource() == addSpace) {
            if (validateSpaceForm()) {
                createNewTab();
            }
        }
    }

    private void createNewTab() {
        Tab createdTab = new Tab();
        Integer level = null;
        try {
            level = createNewTabFxmlSettings(createdTab);
        } catch (LevelExistException ex) {
            Logger.getLogger(ConfiguratorPanelController.class.getName()).log(Level.SEVERE, "[CarPark] This spaces level already exists.");
            createWarningPopup("Ten poziom został już utworzony");
            return;
        }
        createdTab.setContent(createGridPane(Integer.parseInt(spaceRows.getText()), Integer.parseInt(spaceColumns.getText()), level, null));
        addTabToSortedPlace();
    }

    private Integer createNewTabFxmlSettings(Tab createdTab) throws LevelExistException {
        String createdTabName = spaceLevel.getValue().toString();
        StringBuilder sb = new StringBuilder(createdTabName);
        String id = sb.substring(7, sb.length());
        createdTab.setText(createdTabName);
        createdTab.setId(id);
        if (tabs.containsKey(id)) {
            throw new LevelExistException();
        }
        tabs.put(id, createdTab);

        return Integer.parseInt(id);
    }

    private Tab createNewTab(String id, Integer x, Integer y, List<FieldData> data) {
        Tab createdTab = new Tab();
        Integer level = null;
        try {
            level = createNewTabFxmlSettings(createdTab, id);
        } catch (LevelExistException ex) {
            Logger.getLogger(ConfiguratorPanelController.class.getName()).log(Level.SEVERE, "[CarPark] This spaces level already exists.");
            createWarningPopup("Ten poziom został już utworzony");
            return null;
        }
        createdTab.setContent(createGridPane(y, x, level, data));
        addTabToSortedPlace();
        return createdTab;
    }

    private Integer createNewTabFxmlSettings(Tab createdTab, String id) throws LevelExistException {
        createdTab.setId(id);
        createdTab.setText("Poziom "+id);
        if (tabs.containsKey(id)) {
            throw new LevelExistException();
        }
        tabs.put(id, createdTab);

        return Integer.parseInt(id);
    }

    private ScrollPane createGridPane(int rows, int columns, Integer level, List<FieldData> data) {
        ScrollPane scroll = new ScrollPane();
        GridPane grid = new GridPane();
        grid.setMinWidth(Control.USE_COMPUTED_SIZE);
        grid.setPrefWidth(PREF_GRID_WIDTH);
        grid.paddingProperty().setValue(new Insets(20));
        for (int i = 0; i < columns; i++) {
            for (int j = 0; j < rows; j++) {
                final SpaceButton oneSpace = new SpaceButton(i, j, level);
                oneSpace.setOnAction(new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent t) {
                        if (isSpaceButtonIdParsableToInt(oneSpace.getId())) {
                            if (fields.containsKey(Integer.parseInt(oneSpace.getId()))) {
                                showSpaceSettingsPopup(oneSpace, fields.get(Integer.parseInt(oneSpace.getId())));
                            }
                        } else {
                            showSpaceSettingsPopup(oneSpace, null);
                        }
                    }
                });
                oneSpace.setMinWidth(30);
                oneSpace.setMinHeight(30);
                oneSpace.setPrefWidth(50);
                oneSpace.setPrefHeight(50);
                if (data != null) {
                    FieldData dataForBtn = isElementForCell(data, i, j);
                    if (dataForBtn != null) {
                        oneSpace.getStyleClass().add(SPACE_DEFAULT_STYLE);
                        dataForBtn.setToButton(oneSpace);
                        fields.put(dataForBtn.getSpaceNumber(), dataForBtn);
                    } else {
                        oneSpace.getStyleClass().add(SPACE_DEFAULT_STYLE);
                    }
                } else {
                    oneSpace.getStyleClass().add(SPACE_DEFAULT_STYLE);
                }
                grid.add(oneSpace, i, j);
            }
        }
        scroll.setContent(grid);

        return scroll;
    }

    public FieldData isElementForCell(List<FieldData> data, int x, int y) {
        for (FieldData currentData : data) {
            if (currentData.getX() == x && currentData.getY() == y) {
                return currentData;
            }
        }
        return null;
    }

    public boolean showSpaceSettingsPopup(final SpaceButton space, FieldData oldData) {
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getClassLoader().getResource("popupSpaceSettings.fxml"));
            AnchorPane page = (AnchorPane) loader.load();
            final Stage dialogStage = new Stage();
            dialogStage.setTitle("Ustawienia stanowiska");
            dialogStage.initModality(Modality.WINDOW_MODAL);
            Scene scene = new Scene(page);
            dialogStage.setScene(scene);
            final PopupSpaceSettingsController controller = loader.getController();
            if (oldData != null) {
                controller.putOldData(oldData);
            }
            controller.getSaveSpace().setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent t) {
                    FieldData data = controller.getDataFromPopup(space.getX(), space.getY(), space.getSpaceLevel());

                    if (fields.containsKey(data.getSpaceNumber())) {
                        if (isSpaceButtonIdParsableToInt(space.getId())) {
                            if (Integer.parseInt(space.getId()) == data.getSpaceNumber()) {
                                if (data.setToButton(space)) {
                                    fields.put(data.getSpaceNumber(), data);
                                    dialogStage.close();
                                } else {
                                    fields.remove(data.getSpaceNumber());
                                }
                            } else {
                                dialogStage.close();
                                createWarningPopup("Istnieje stanowisko o podanym numerze.");
                            }
                        } else {
                            dialogStage.close();
                            createWarningPopup("Istnieje stanowisko o podanym numerze.");
                        }
                    } else {
                        if (data.setToButton(space)) {
                            fields.put(data.getSpaceNumber(), data);
                        }
                        dialogStage.close();
                    }
                    dialogStage.close();

                }
            });
            dialogStage.showAndWait();

            return true;

        } catch (IOException e) {
            Logger.getLogger(ConfiguratorPanelController.class.getName()).log(Level.SEVERE, "[CarPark] Cannot load space settings popup.", e);
            return false;
        }
    }

    private boolean isSpaceButtonIdParsableToInt(String id) {
        if (id == null) {
            return false;
        }
        try {
            Integer.parseInt(id);
        } catch (Exception e) {
            Logger.getLogger(ConfiguratorPanelController.class.getName()).log(Level.SEVERE, "[CarPark] Cannot parse space button id to integer.", e);
            return false;
        }

        return true;
    }

    private void createWarningPopup(String text) {
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getClassLoader().getResource("popup.fxml"));
            AnchorPane page = (AnchorPane) loader.load();
            Stage dialogStage = new Stage();
            dialogStage.setTitle("Potwierdzenie");
            dialogStage.initModality(Modality.WINDOW_MODAL);
            Scene scene = new Scene(page);
            dialogStage.setScene(scene);

            PopupController controller = loader.getController();
            controller.setDialogStage(dialogStage);
            controller.setPopupText(text);
            dialogStage.showAndWait();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void addTabToSortedPlace() {
        tabs.put(addTab.getId(), addTab);
        List<Map.Entry<String, Tab>> entries = new ArrayList<>(tabs.entrySet());
        Collections.sort(entries, new Comparator<Map.Entry<String, Tab>>() {
            @Override
            public int compare(Map.Entry<String, Tab> a, Map.Entry<String, Tab> b) {
                if (Integer.parseInt(a.getKey()) == Integer.parseInt(b.getKey())) {
                    return 0;
                }
                if (Integer.parseInt(a.getKey()) > Integer.parseInt(b.getKey())) {
                    return -1;
                }
                return 1;
            }
        });
        levelTabs.getTabs().clear();
        for (Map.Entry<String, Tab> entry : entries) {
            levelTabs.getTabs().add(entry.getValue());
        }

    }

    private boolean validateSpaceForm() {
        boolean isCorrect = true;
        if (spaceRows.getText() == null) {
            changeSpaceRowsStyle(false);
            isCorrect = false;
        }
        if (spaceColumns.getText() == null) {
            changeSpaceColumnsStyle(false);
            isCorrect = false;
        }
        if (spaceLevel.getValue() == null) {
            changeSpaceLevelStyle(false);
            isCorrect = false;
        } else {
            changeSpaceLevelStyle(true);
        }
        if (isTextParsableAndInBorders(spaceRows.getText())) {
            changeSpaceRowsStyle(true);
        } else {
            changeSpaceRowsStyle(false);
            isCorrect = false;
        }
        if (isTextParsableAndInBorders(spaceColumns.getText())) {
            changeSpaceColumnsStyle(true);
        } else {
            changeSpaceColumnsStyle(false);
            isCorrect = false;
        }
        return isCorrect;
    }

    private boolean isTextParsableAndInBorders(String text) {
        Integer value = null;
        try {
            value = Integer.parseInt(text);
        } catch (NumberFormatException ex) {
            return false;
        }
        return (value > MIN_SIZE && value <= MAX_SIZE);
    }

    private void changeSpaceRowsStyle(boolean good) {
        if (good) {
            if (spaceRows.getStyleClass().remove(WRONG_INFO_STYLE)) {
                spaceRows.getStyleClass().add(GOOD_INFO_STYLE);
                spaceRowInfo.setText(EMPTY_STRING);
            }
        } else {
            if (spaceRows.getStyleClass().remove(GOOD_INFO_STYLE)) {
                spaceRows.getStyleClass().add(WRONG_INFO_STYLE);
                spaceRowInfo.setText(ROWS_INFO);
            }
        }
    }

    private void changeSpaceColumnsStyle(boolean good) {
        if (good) {
            if (spaceColumns.getStyleClass().remove(WRONG_INFO_STYLE)) {
                spaceColumns.getStyleClass().add(GOOD_INFO_STYLE);
                spaceColumnInfo.setText(EMPTY_STRING);
            }
        } else {
            if (spaceColumns.getStyleClass().remove(GOOD_INFO_STYLE)) {
                spaceColumns.getStyleClass().add(WRONG_INFO_STYLE);
                spaceColumnInfo.setText(COLUMNS_INFO);
            }
        }
    }

    private void changeSpaceLevelStyle(boolean good) {
        if (good) {
            if (spaceLevel.getStyleClass().remove(WRONG_INFO_STYLE)) {
                spaceLevel.getStyleClass().add(GOOD_INFO_STYLE);
                spaceLevelInfo.setText(EMPTY_STRING);
            }
        } else {
            if (spaceLevel.getStyleClass().remove(GOOD_INFO_STYLE)) {
                spaceLevel.getStyleClass().add(WRONG_INFO_STYLE);
                spaceLevelInfo.setText(CHOOSE_LEVEL_INFO);
            }
        }
    }
}
